#include <iostream>
#include <string>
#include <vector>
#include <cassert>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "task.hpp"

#define COUNT_LOOP 0
#define ALWAYS_LOOP 1

const int process_num = 5;

static int channel_num = 1;

class channel 
{
public:
    channel(int fd, pid_t id): ctrl_fd(fd), worker_id(id) 
    {
        name = "channel-" + std::to_string(channel_num++);
    }

public:
    int ctrl_fd;
    pid_t worker_id;
    std::string name;
};

void work() 
{
    while(true) 
    {
        int code = 0;
        ssize_t n = read(0, &code, sizeof(code));
        if(n == sizeof(code)) 
        {
            if(tasks.checkSafe(code) == true)
            {
                tasks.runTask(code);
            } 
        }
        else if(n == 0)
        {
            break;
        }
    }
    std::cout << "child quit: " << getpid() << std::endl;
}

//传参约定
// 输入型参数：const &
// 输出型参数：*
// 输入输出型参数：&
void createChannels(std::vector<channel>* channels)
{
    //循环创建父向子写的管道，会使从第二次开始fork出的子进程，继承前面所有管道的写端，仅最后一个管道只有父进程一个写端

    //谨慎回收：
    //想回收管道和子进程，需要关闭对应的所有写端，使读端read返回0，跳出循环，进程退出
    //法一：用两个循环，先关完写端，让最后一个子进程释放，连锁所有子进程便倒序释放，最后统一waitpid
    //法二：用一个循环，直接倒着回收

    //正面解决：
    //子进程创建后，关闭历史信道
    std::vector<int> tmp;
    for(int i = 0; i < process_num; i++) 
    {
        //创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n == 0);

        //创建子进程
        pid_t id = fork();
        assert(id != -1);

        //子进程
        if(id == 0) 
        {
            if(tmp.empty() == false)
            {
                for(auto fd : tmp)
                {
                    close(fd);
                }
            }
            close(pipefd[1]);
            dup2(pipefd[0], 0);
            work();
            exit(0);
        }

        //父进程
        close(pipefd[0]);
        channels->push_back(channel(pipefd[1], id));
        tmp.push_back(pipefd[1]);
    }
}

void releaseChannels(const std::vector<channel>& channels)
{
    for(const auto& channel : channels)
    {
        close(channel.ctrl_fd);
        pid_t rid = waitpid(channel.worker_id, nullptr, 0);
        if(rid == channel.worker_id)
        {
            std::cout << "wait success: " << channel.worker_id << std::endl;
        }
    }
}

//法一
void releaseChannels1(const std::vector<channel>& channels)
{
    for(const auto& channel : channels)
    {
        close(channel.ctrl_fd); //写端关闭，管道释放，子进程退出
    }
    for(const auto& channel : channels)
    {
        pid_t rid = waitpid(channel.worker_id, nullptr, 0);
        if(rid == channel.worker_id)
        {
            std::cout << "wait success: " << channel.worker_id << std::endl;
        }
    }
}

//法二
void releaseChannels2(const std::vector<channel>& channels)
{
    for(int i = channels.size()-1; i >= 0; i--)
    {
        close(channels[i].ctrl_fd);
        pid_t rid = waitpid(channels[i].worker_id, nullptr, 0);
        if(rid == channels[i].worker_id)
        {
            std::cout << "wait success: " << channels[i].worker_id << std::endl;
        }
    }
}

void sendCommand(const std::vector<channel>& channels, int loop_mode, int loop_num = 1)
{
    int pos = 0;
    while(loop_num)
    {
        //选择任务
        int command = tasks.selectTask();
        //选择信道
        const channel& c = channels[pos++];
        pos %= channels.size();
        //发送任务
        write(c.ctrl_fd, &command, sizeof(command));
        if(loop_mode == COUNT_LOOP) loop_num--;
        if(loop_num == 0) break;
        sleep(1);
    }
}

void printDebug(const std::vector<channel>& channels)
{
    for(const auto& channel : channels)
    {
        std::cout << channel.name << ", " << channel.ctrl_fd << ", " << channel.worker_id << std::endl;
    }
}

int main()
{
    std::vector<channel> channels;

    //创建信道与子进程
    createChannels(&channels);

    //发送任务
    sendCommand(channels, COUNT_LOOP, 10);
    //sendCommand(channels, ALWAYS_LOOP);

    //回收子进程
    releaseChannels(channels);

    //printDebug(channels);
    return 0;
}